|
Technote 1154Debugging Java Code With MacsBugBy Jens Alfke |
CONTENTSMacsBug In a Nutshell |
M acsBug, the
low-level debugger for the Mac OS, seems unlikely to be
useful for debugging a very high-level language like Java.
Au contraire! The MRJ plug-in |
MacsBug In a NutshellBefore you can start using MacsBug to debug Java, you need to install MacsBug and learn the basics of how to use it. If you’ve already been developing Mac software, this is a non-issue since you’re almost certainly already familiar with MacsBug, and you can skip to the next section. However, there are a lot of people developing or testing Java on the Macintosh who are not otherwise Mac developers and don’t know an A-trap from “Take the A Train.” This section is for them, so it presumes a lot less Mac technical knowledge than most technotes do. What’s MacsBug?MacsBug is Apple’s assembly-level 680x0 and PowerPC debugger for Mac OS. It can be used to debug code running in most execution environments, from applications to drivers, and everything in between. It’s often used as a bug-reporting tool by many third-party developers, as well as Mac OS system software developers. MacsBug knows nothing about source code, only assembly-language instructions, and its support for high-level data structures is primitive. But it’s great for examining the exact machine state. Unlike debuggers on most other platforms, MacsBug is always resident once installed, and can take over instantly when a crash occurs or when you press a hot-key, even if the machine is otherwise frozen. Download & InstallationMacsBug is available from Apple’s developer Web site. As befits a tweaky developer tool, it spends most of its time in one prerelease state or another, and the latest and greatest version is nearly always marked as alpha or beta. Nonetheless, it’s still usually best to install the latest prerelease; they’ve proven to be pretty stable. (Note in particular that as of this writing, only prerelease versions are compatible with OS 8.5.) There are a lot of files in the download, but the only one you need is MacsBug itself, which you’ll find in the “into System folder” folder. Drag it into your System folder (not the Extensions folder!) and restart. You should see the message “Debugger Installed” appear below “Welcome To Mac OS” in the Mac OS splash screen as the system begins to boot. A Very Brief OverviewThe MacsBug manual is also available online, but it’s large and intimidating (not to mention slightly obsolete) and actually overkill for the Java-only developer. Here’s a very brief introduction: MacsBug loads itself into memory very early in the boot process and hides out invisibly until it’s needed. (It does consume a bit of RAM.) Three different circumstances will cause MacsBug to appear:
When MacsBug appears, it completely takes over the screen and the CPU. No other application or OS software can run while MacsBug is visible. This explains why MacsBug’s user interface is so completely non-Mac-like -- it can’t use any of the Toolbox. MacsBug is a command-line environment like DOS or a Unix shell. It shows one fixed-size window in the middle of the screen, with garbage pixels outside the window. There’s an input line at the bottom, a few lines of machine code disassembly above it, and a large scrolling output area above that. On the left side is a register and stack display. You type commands into the input line at the bottom and press Return to run them. You can’t use the mouse to select text, but most standard editing keystrokes work (arrow keys, delete and forward-delete, option- and Command- arrow, etc.) You can also press Cmd-V to copy the previous command into the input line. Some essential commandsThe most vital MacsBug commands are:
|
The 'mrj' dcmdWe’ve written a plug-in (called a “dcmd”) for MacsBug
that adds a new command, The To use the
|
Here’s information about the most commonly used commands. (Many of the other commands are for querying internal data structures and are only of use for debugging MRJ itself.) If you invoke the Java log (
|
mrj il
methodname
)Disassembles the bytecodes of a method. The method has to be named in the usual internal format:
/
”s.
”,.
”For example:
il java/lang/Thread.join.()V disassembly from $659ed84 java.lang.Thread.join(Thread.java:873) [0] 2A aload_0 [1] 9 lconst_0 [2] E2 invokevirtual_quick_w Method: "java/lang/Thread.join(J)V" [5] B1 return |
The debug build of MRJ enables extra commands in the MRJ
The debug build also has a limited form of
deadlock-checking built into the thread scheduler: in the
case of a classic two-thread deadlock it will automatically
drop into MacsBug with a user break warning about a
deadlock. You should immediately use “ Another handy feature of the debug build is that it will
display a cursor shaped like a little bulldozer while it
garbage-collects. This can help you tell whether long pauses
in your app are actually caused by garbage collection (as
can the Count class instances (
|
Warning: |
This command waits for the main MRJ thread to get
control, does its work,
then writes the results to
System.out
. Here’s the beginning of some typical output:
mrj graphrefs $6b11f18 recursively searching for references to $6b11f18 References to: $6b11f18 instance field: $6b11aa8 java/lang/ Thread.target(Ljava/lang/Runnable;) instance field: $6b126c8 com/apple/mrj/console/Console$ConsoleArea.this$0(Lcom/ apple/mrj/console/Console;) instance field: $6b13628 com/apple/mrj/ console/Console$1.this$0(Lcom/apple/mrj/console/Console;) java thread var ref $6b11f18 at $x (tid $68ef584, ) References to: $6b11aa8 array element: $6b11ea8 [1] c thread found $6b11aa8 at $680f2fc (tid $6adda7c, ConsoleThread) c thread found $6b11aa8 at $680f34c (tid $6adda7c, ConsoleThread) c thread found $6b11aa8 at $680f370 (tid $6adda7c, ConsoleThread) c thread found $6b11aa8 at $680f3e8 (tid $6adda7c, ConsoleThread) java thread var ref $6b11aa8 at $x (tid $68ef558, ) |
Traces are separated by blank lines. Each trace starts
with “ The first trace is for the object you requested. Subsequent traces are for objects found in previous traces. The result, if you follow from one trace to another, lets you find out exactly what chains of references are keeping an object from being garbage collected. This output is pretty hard to read and cries out for a nice tool to interpret it. For now all we can suggest is pasting the output into a good programmer’s editor and using the Find command to find matching hex values. |
Interpreting Stack Crawls and Thread DumpsThe
The thread headerThe first four lines display information about the thread: “The thread’s name” In quotes on the first line. This is the String parameter passed to the thread’s constructor. If you don’t provide one, you get a default name like “Thread-7.” Giving your threads meaningful names is quite useful when debugging.
The stack crawl itselfAfter the thread header comes the Java stack crawl. This is mostly identical to the kind of stack crawl you’re used to seeing when an exception is dumped to the console. The stack frames are listed in reverse chronological order, so the current method is at the top. There’s an additional hex number at the beginning of each
line, which is the object handle of the “ After the object handle comes the name of the method. After that in parentheses is the name of the source file and the exact line number.
The Monitor Cache DumpMonitors are the primitives used to implement synchronization on objects. Monitors are not objects, but an object is assigned a monitor when a thread synchronizes against it. (There are also special internal monitors that are not associated with objects.) As described above, the header of a stack crawl tells
whether the thread is blocked on a monitor and, if so, which
one. The thing you probably want to know next is what object
that monitor corresponds to. Usually (not always) you can
determine this by looking in the Monitor Cache Dump section
in the “
The first line shows the object’s class. The hex number
between the “ The second line shows which thread owns (is currently
holding) that monitor, or If one or more other threads are blocked on that monitor,
they will be listed after a line reading “ It’s worth pointing out two Java objects that often play
a prominent role in synchronization problems.
The Registered Monitor DumpThe last section of the thread dump shows the list of
registered monitors. These are monitors that are not
associated with objects but which are known to the JVM.
These are used internally by things like the JIT, the class
loader, and the finalizer thread. Normally you don’t need to
pay attention to these, but very rarely you may encounter a
deadlock that involves one or them (for instance, we once
had a nasty bug that could cause the JIT and the class
loader to deadlock). If you encounter any problems involving
these, it’s almost certainly a bug in MRJ, which you should
report at once, including a |
Switching ContextsIf only a single instance of MRJ is running, the To target a particular instance of MRJ, you need to know
its CFM context ID. You can find this by using the
MacsBug “ Switch contexts (
|
Further References
|
AcknowlegmentsI’d like to thank Mikey McDougall, Sue Manning, and Steve Zellers for their help in explaining to me the intricate details of many of these commands, and Steve McGrath for further review. |